import { compare, getThreeBezierPoint, getCircularPos } from "./common.js";
import Custom from "./figure/custom.js";
import Connect from "./route/index.js";
import Shape from "./baseShape/index.js";

// 绘制方法（包括了几个默认的图形：矩形、圆、线）
// 后续特殊图形放到专门的方法中
export default class Draw extends Shape {
  constructor({ highlightInfo }) {
    super();
    this.Connect = new Connect(this);
    this.Custom = new Custom(this); // 自定义绘制图案的实例
    this.init();
    this.imageMap = new Map(); // 永远不会被清空，否则会导致 img.onload 重复调用
    // this.subX = -width/2;
    // this.subY = -height/2;

    /* 
      自定义高亮信息 （目前只有高亮颜色，后续可能会加入其他的高亮配置）
    */
    if (highlightInfo) {
      this.highlightColor = highlightInfo.fillColor || "rgb(0,217,255)";
    } else {
      this.highlightColor = "rgb(0,217,255)";
    }
  }

  init() {
    this.linkArr = []; // 页面中所存在的连接点的集合
    this.sign = [];
    this.boardCanvas = {};
    this.boardSign = {};
    this.r = {};
    this.g = {};
    this.b = {};

    // 最主要的两组数据
    this.canvasInfoMap = new Map(); // 当前画布中存在的元素信息（图案的详细信息，包括长宽高、坐标等）  * 注意：此处存放的数据可以用来直接渲染图形
    this.connectInfo = []; // 当前画布中已经存在的连接线信息（存储图案之间的连接关系，方便计算使用） * 注意：这里存放的原始数据

    // 绘制图案时用到的变量
    this.type = null; // 要绘制的图案类型
    this.customProps = {}; // 每次在绘制图案时携带的自定义属性（会在绘制前填充到图案信息里面）
    this.auxProps = {}; // 每次在绘制时执行的辅助属性（ 绘制前将画布状态保存 save() ，绘制后还原 restore() ）
    this.validateProp = [
      "x",
      "y",
      "endX",
      "endY",
      "width",
      "height",
      "radiusX",
      "radiusY",
    ]; // 这些属性不能被当成“自定义属性”传入
    this.validateAuxProp = ["globalCompositeOperation"]; // 允许添加的属性

    this.subX = 0;
    this.subY = 0;
    this.connectType = "connectCurve"; // 要绘制的连接线的类型
    this.connectLineEle = {};
    this.ele = {}; // 当前选中的元素
    this.rgbMap = new Map();
    this.stagingMap = {}; // 一些临时性存储的东西
    this.maxLevel = 0; // 画布元素的最大层级

    // sign.js
    this.mouseX = 0;
    this.mouseY = 0;
    this.originX = 0; // 记录中心点
    this.originY = 0;
    this.dragStartX = 0;
    this.dragStartY = 0;
    this.mouseRelativeX = 0; // 记录鼠标离图形中心的相对位置（方便计算旋转角）
    this.mouseRelativeY = 0;
    this.signWidth = 8;
    this.signHeight = 8;
    this.startRotate = 0;
    this.endRotate = 0;
    this.PropAboutX = ["x", "endX", "minX", "maxX"];
    this.PropAboutY = ["y", "endY", "minY", "maxY"];
    this.nodeTimeout = null;
    this.connectTimeout = null;
    this.highlightEle = "";
    this.idMap = new Map();
    this.addImgList = [];
    this.toTop = null; // 当前点击的元素置顶显示（放到最后一个加载）
    this.r = 0;
    this.g = 0;
  }

  // 删除图形（默认删除当前选中的图形）
  deleteNode(id) {
    if (typeof id === "object" && String(id) !== "null")
      throw new Error("id should be string");
    if (!id && !this.ele && !this.connectLineEle)
      throw new Error("node not has been selected"); // 涉及到鼠标的点击/移动操作，所以将选中的图案和选中的连接线分成了两个对象存储
    let chooseEle = this.ele || this.connectLineEle;

    let nodeId = id || chooseEle.id;

    let newConnectArr = [];
    // 获取到相关的连接线id（和当前所选中图案相关的连接线id）
    let relatedElementIds = this.connectInfo.map((i) => {
      if (i.startId === nodeId || i.targetId === nodeId || i.id === nodeId) {
        return i.id;
      } else {
        newConnectArr.push(i);
        return null;
      }
    });
    relatedElementIds.push(nodeId);
    // 删除所有和当前被删除图案有关联的连接线
    this.connectInfo = newConnectArr;
    relatedElementIds.forEach((i) => {
      this.canvasInfoMap.delete(i);
    });

    this.sign = [];
    this.ele = {};
    this.clearProps();
    this.isDragElement = false; // 点击空白地方，取消对某个图形的选中
    this.repaintBackboard();
    this.repaint();
  }

  // 对场景节点进行排序
  sortNodes(id) {
    let topEle = null;
    if (id) {
      topEle = [id, this.canvasInfoMap.get(id)];
      this.canvasInfoMap.delete(id);
    }
    let arr = Array.from(this.canvasInfoMap);
    arr.sort((a, b) => {
      return a[1].level - b[1].level;
    });
    if (topEle) arr.push(topEle);
    this.canvasInfoMap = new Map(arr);
  }

  // 重绘画布上的图案（不包括连接线）
  repaint(jump = () => {}) {
    this.empty();
    if (!this.canvasInfoMap.size) {
      jump();
      return;
    }
    if (this.shadeLayer) {
      this.repaintShade();
      return;
    }
    // let customCanvas = [];
    // 排序功能
    this.sortNodes(this.ele ? this.ele.id || null : null);

    this.maxLevel = 0;
    let arr = [];
    if (this.canvasInfoMap.size) arr = [...this.canvasInfoMap.values()];
    this.toTop = null; // 当前点击的元素置顶显示（放到最后一个加载）
    let index = 0;
    this.addByPromise(arr, index, () => {
      if (this.toTop) {
        this.repaintEle(this.toTop);
      }
      jump();
    });

    // let promise = [];
    // for(let i of arr){
    //   if(i.level > this.maxLevel) this.maxLevel = i.level;
    //   if(this.ele && i.id === this.ele.id){
    //     toTop = i;
    //   } else if (i.type === 'custom'){
    //     customCanvas.push(i)
    //   } else {
    //     promise.push(new Promise((resolve) => {
    //       this.repaintEle(i, resolve());
    //     }))
    //   }
    // }

    // Promise.all(promise).then(() => {
    //   jump();
    //   customCanvas.forEach((i) => {
    //     this.customDraw(i)
    //   })
    //   if(toTop) {
    //     this.repaintEle(toTop)
    //   }
    // })
  }
  // 依次渲染图案
  addByPromise(nodes, index, callback) {
    let node = nodes[index];
    if (node) {
      if (node.level > this.maxLevel) this.maxLevel = node.level;
      new Promise((resolve) => {
        if (this.ele && node.id === this.ele.id) {
          this.toTop = node;
          resolve();
        } else if (node.type === "custom") {
          this.customDraw(node);
          resolve();
        } else {
          this.repaintEle(node, resolve);
        }
      }).then(() => {
        index++;
        if (index < nodes.length) {
          this.addByPromise(nodes, index, callback);
        } else {
          this.maxLevel = 0;
          callback();
        }
      });
    }
  }
  // 重绘图案
  repaintEle(i, resolve = () => {}) {
    if (["rect", "image"].includes(i.type)) {
      this.Rect.rect(i, null, null, resolve);
      if (i.type === "rect") resolve();
    } else if (i.type === "line") {
      this.Line.line(i, null, null);
      resolve();
    } else if (i.type === "circular") {
      this.Circular.circular(i, null, null);
      resolve();
    } else if (["connectCurve", "connectArrowCurveFill"].includes(i.type)) {
      this.threeCurve(i, null, null);
      resolve();
    }

    if (this.loadText && i.textInfo) {
      this.drawText(i);
    }
  }
  // 重绘遮罩画布
  repaintShade() {
    if (this.ele) {
      let ele = this.canvasInfoMap.get(this.ele.id);
      if (ele) this.repaintEle(ele);
    }
  }

  // 重新创建连接线（为了防止两个连接点位置发生改变，每次重新创建都需要重新计算连接线的信息）
  repaintConnect(id, save = true) {
    let startEle;
    let targetEle;
    let startSign;
    let targetSign;
    let startCenter;
    let targetCenter;
    let res;
    let startObj;
    let endObj;

    // 只更新与当前图案相关的连线
    let connects;
    if (id) {
      connects = this.connectInfo.filter((i) => i.id === id);
    } else {
      connects = this.connectInfo.filter(
        (i) => i.startId === this.ele.id || i.targetId === this.ele.id
      );
    }
    if (connects.length) {
      for (let i of connects) {
        let { id, type, startInfo, targetInfo } = i;
        // 根据图形id获取两个被连接图形
        startEle = this.canvasInfoMap.get(startInfo.id);
        targetEle = this.canvasInfoMap.get(targetInfo.id);

        // 找到两个图案中被连接的两个点（使用 getCanvasCenterPoint 函数获取 originX, originY）
        startCenter = this.getCenterPoint(
          startEle,
          this.getCanvasCenterPoint(startEle)
        );
        targetCenter = this.getCenterPoint(
          targetEle,
          this.getCanvasCenterPoint(targetEle)
        );

        // 判断当前改变形状的图案是起点还是终点（因为矩形缩放的时候中心点是固定的）
        startObj = { centerX: startCenter.x, centerY: startCenter.y };
        endObj = { centerX: targetCenter.x, centerY: targetCenter.y };

        // 获取开始/结束点的连接点信息，并将连接点的信息放到两个图形的配置中
        Object.assign(
          startEle,
          startObj,
          this.getQuietConnectInfo({
            ...startEle,
            signWidth: this.signWidth,
            signHeight: this.signHeight,
          })
        );
        Object.assign(
          targetEle,
          endObj,
          this.getQuietConnectInfo({
            ...targetEle,
            signWidth: this.signWidth,
            signHeight: this.signHeight,
          })
        );

        // 获取两个连接点的坐标等信息
        startSign = this.drawBorderConnect(startEle)[startInfo.pos];
        targetSign = this.drawBorderConnect(targetEle)[targetInfo.pos];

        // 清理掉 canvasInfo 中原来的连接线
        this.replaceConnect(id);
        // 根据连接线的类型来重新计算坐标
        if (type === "connectCurve" || type === "connectArrowCurveFill") {
          res = this.Connect.getThreeCurve({
            startSign: { ...startSign, id: startEle.id },
            targetSign: { ...targetSign, id: targetEle.id },
            type,
          });
          res.id = id;
          res.textInfo = i.textInfo || {};
          res.fillColor = i.fillColor;
          res.fillType = i.fillType;
          res.lineWidth = i.lineWidth || 1;
          delete res.signCenterX;
          delete res.signCenterY;
          this.threeCurve(res, save); // 绘制连接线
        }
      }
    }
  }

  // 清空画布
  empty() {
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
  }

  // 选中图形后，拖拽功能（每次拖拽都会调用此方法来更新元素信息）
  // mousemove 时使用
  // ele: 被拖拽的元素
  dragElement(ele) {
    this.replaceEle(ele); // 替换图案原有信息

    new Promise((resolve) => {
      this.repaint(resolve); // 更新画布
    }).then(() => {
      if (
        !["connectCurve", "connectArrowCurveFill"].includes(ele.type) &&
        !this.shadeLayer
      )
        this.drawBorder(ele); // 添加图案的缩放框
      this.clearProps();
    });
  }

  // 移动画布
  dragCanvas({ x, y }) {
    this.canvas.style["margin-top"] = y + "px";
    this.canvas.style["margin-left"] = x + "px";
  }

  // 替换原有图案信息
  replaceEle(ele) {
    if (this.canvasInfoMap.get(ele.id)) this.canvasInfoMap.set(ele.id, ele);
    if (["connectCurve", "connectArrowCurveFill"].includes(ele.type)) {
      let connect = this.connectInfo.filter((i) => i.id === ele.id);
      if (connect.length) {
        Object.assign(connect[0], ele);
        // connect[0].textInfo = ele.textInfo
      }
    }
  }

  // 清理原有的连接线
  replaceConnect(id) {
    this.canvasInfoMap.delete(id);
  }

  // 重新画移动过的图案（在改变图像原有方向/大小以后，要删掉当前选中的元素重新画，目的为了能重新计算图形的属性，方便后续使用）
  reloadElement({ id }) {
    // 清除图案信息
    let element = this.canvasInfoMap.get(id);
    if (!element) return {};

    if (["connectCurve", "connectArrowCurveFill"].includes(element.type))
      return element; // 如果是连接线，则直接返回（因为连线会在每次鼠标抬起时自动重绘，防止计算不准确）

    this.canvasInfoMap.delete(id);

    let ele;

    if (element.type === "line") {
      ele = this.Line.line(element, true, false);
    } else if (["rect", "image"].includes(element.type)) {
      ele = this.Rect.rect(element, true, false);
      // , true, false
    } else if (element.type === "circular") {
      ele = this.Circular.circular(element, true, false);
    }

    if (element.textInfo) this.drawText(ele);
    return ele;
  }

  // 线的绘制入口
  drawLine({ offsetX, offsetY, startX, startY, level = 1 }, save = true) {
    let endX = offsetX;
    let endY = offsetY;
    this.Line.line(
      {
        x: startX || this.mouseX,
        y: startY || this.mouseY,
        endX,
        endY,
        level,
        fillColor: this.defaultColor,
        lineWidth: this.defaultWidth,
        id: this.id || new Date().getTime(),
        ...this.customProps,
        auxProps: this.auxProps,
      },
      save
    );
  }

  // 矩形绘制入口
  drawRect(val, save, reload) {
    let width = val.offsetX - this.mouseX;
    let height = val.offsetY - this.mouseY;
    let x = this.mouseX;
    let y = this.mouseY;
    let level = val.level || 1;
    if (width < 0) {
      width = -width;
      x = val.offsetX;
    }
    if (height < 0) {
      height = -height;
      y = val.offsetY;
    }
    this.Rect.rect(
      {
        id: this.id,
        x,
        y,
        width,
        height,
        level,
        fillColor: this.defaultColor,
        lineWidth: this.defaultWidth,
        textInfo: val.textInfo,
        type: "rect",
        ...this.customProps,
        auxProps: this.auxProps,
      },
      save,
      reload
    );
  }

  // 圆的绘制入口
  drawCircular(val, save, reload) {
    let radiusX = (val.offsetX - this.mouseX) / 2;
    let radiusY = (val.offsetY - this.mouseY) / 2;
    let x = this.mouseX + radiusX;
    let y = this.mouseY + radiusY;
    let level = val.level || 1;

    // 切记：无论如何都不要让半径这种等信息为负数
    if (radiusX < 0) {
      radiusX = -radiusX;
      x = this.mouseX - radiusX;
    }
    if (radiusY < 0) {
      radiusY = -radiusY;
      y = this.mouseY - radiusY;
    }

    this.Circular.circular(
      {
        id: this.id,
        x,
        y,
        radiusX,
        radiusY,
        level,
        fillColor: this.defaultColor,
        lineWidth: this.defaultWidth,
        textInfo: val.textInfo,
        ...this.customProps,
        auxProps: this.auxProps,
      },
      save,
      reload
    );
  }

  // 清除某些信息
  clearProps() {
    this.setNodeType("");
    this.customProps = {};
    this.auxProps = {};
    this.drawCanvas = null;
  }

  // 初始化某些再计算过程中用到的属性
  initProps() {
    this.isDragElement = false; // 点击空白地方，取消对某个图形的选中
    this.subX = 0;
    this.subY = 0;
    this.mouseX = 0;
    this.mouseY = 0;
    this.linkArr = [];
  }

  // 处理画布状态
  // ctx: 要操作的画布
  // rotate: 弧度    originX/originY: 中心点
  setStore(ctx, { rotate = 0, originX = 0, originY = 0 }, callback) {
    ctx.save();
    ctx.translate(originX, originY); // 重新定义当前坐标系原点
    ctx.rotate(rotate);
    callback();
    ctx.restore();
  }

  // 加载图片
  loadImage(
    info,
    { startX = 0, startY = 0, originX = 0, originY = 0 },
    callback = () => {}
  ) {
    let { width = 60, height = 40, src, rotate = 0 } = info;

    let layer = this.layer;
    if (this.ele) {
      if (info.id === this.ele.id)
        layer = this.shadeLayer ? this.shadeLayer : this.layer;
    }

    const onload = () => {
      this.setStore(layer, { rotate, originX, originY }, () => {
        layer.drawImage(img, startX, startY, width, height);
      });
      if (this.loadText && info.textInfo) {
        this.drawText(info);
      }
      callback();
    };
    let img = this.imageMap.get(info.id);
    if (!img) {
      img = new Image();
      img.src = src;
      img.onload = () => {
        onload();
        this.imageMap.set(info.id, img);
        this.clearImageMap(); // 调用清理程序，防止存在过多无用的 image 实例
      };
      img.onerror = () => {
        callback();
        throw new Error("image onload fail, check imgUrl:" + src);
      };
    } else {
      onload();
    }
  }

  // 清理多余的image实例
  clearImageMap() {
    let imgIds = [...this.imageMap.keys()];
    for (let i of imgIds) {
      if (!this.canvasInfoMap.has(i)) this.imageMap.delete(i);
    }
  }

  // 绘制三次方贝塞尔曲线（目前仅用于连接点的连线）
  threeCurve(curve, save = true) {
    let {
      id,
      x,
      y,
      x1,
      y1,
      x2,
      y2,
      endX,
      endY,
      startInfo,
      targetInfo,
      start,
      lineWay,
      type,
      textInfo,
      fillType,
      lineWidth,
      fillColor,
    } = curve;
    this.Connect.drawThreeCurve({
      lineWidth,
      fillColor,
      x,
      y,
      x1,
      y1,
      x2,
      y2,
      endX,
      endY,
    }); // 绘制曲线
    if (type === "connectArrowCurveFill")
      this.customDraw({ start, lineWay, fillType, fillColor }); // 绘制连线箭头
    let connect;
    if (save) {
      // 将点与点的关联管理保存下来
      if (!id) id = this.getUniqueId();
      connect = {
        id: id,
        type,
        x,
        y,
        x1,
        y1,
        x2,
        y2,
        endX,
        endY,
        start,
        lineWay,
        textInfo,
        fillType,
        fillColor,
        lineWidth,
      };
      this.canvasInfoMap.set(id, connect);
      let index = this.connectInfo.findIndex((i) => i.id === id);
      if (index === -1) {
        connect = {
          id,
          type,
          startInfo,
          targetInfo,
          startId: startInfo.id,
          targetId: targetInfo.id,
          start,
          lineWay,
          textInfo,
          fillType,
          fillColor,
          lineWidth,
        };
        this.connectInfo.push(connect);
      }
      if (textInfo) {
        this.drawText(connect);
      }
    }
  }

  // 在图形内添加文字（ x/y 位置动态计算）
  drawText(ele) {
    // * 先找到各种图案的中心点
    let center = { x: 0, y: 0 }; // 图形的中心点
    let maxWidth = 0; // 每个图案的最大宽度
    if (["rect", "image"].includes(ele.type)) {
      center.x = ele.x + ele.width / 2;
      center.y = ele.y + ele.height / 2;
      maxWidth = ele.width;
    } else if (ele.type === "line") {
      center.x = (ele.x + ele.endX) / 2;
      center.y = (ele.y + ele.endY) / 2;
    } else if (ele.type === "circular") {
      let pos = getCircularPos({
        startX: ele.startX || 0,
        startY: ele.startY || 0,
        radiusX: ele.radiusX || 10,
        radiusY: ele.radiusY || 10,
        startAngle: ele.startAngle,
        endAngle: ele.endAngle,
      });
      let { minX, minY, maxX, maxY } = compare(pos);
      center.x = ele.x + (minX + maxX) / 2;
      center.y = ele.y + (minY + maxY) / 2;

      maxWidth = maxX - minX;

      // center.x = ele.x;
      // center.y = ele.y;
    } else if (["connectCurve", "connectArrowCurveFill"].includes(ele.type)) {
      // 曲线这里在找中间点的时候要先切割成10份，找到第五段的末端坐标作为中心点
      let { x, y, x1, y1, x2, y2, endX, endY } = ele;
      let range = getThreeBezierPoint(
        0.5,
        [x, y],
        [x1, y1],
        [x2, y2],
        [endX, endY]
      );
      center.x = range[0];
      center.y = range[1];
    }

    // 处理当前文字大小
    this.Custom.textFont(ele.textInfo);

    // 计算文字换行等
    // x/y: 文字开始位置
    // textArr: 文字数组 ['text1', 'text2]
    // lineHeight: 行高（文字间隔）
    let { x, y, textArr, lineHeight } = this.Custom.textPosition({
      ele,
      maxWidth,
    });
    let textStartXY = { x, y }; // 文字的起始点

    this.ctx.save();
    this.ctx.translate(center.x, center.y);
    this.ctx.rotate(ele.rotate || 0);
    // 根据中心点以及文字信息，找到合适的地方放置
    for (let i of textArr) {
      this.customText({ ...textStartXY, ...ele.textInfo, text: i });
      textStartXY.y += lineHeight;
    }

    this.ctx.restore();
  }

  // 自定义添加文字
  customText({ x, y, text, font, fillType, fillColor = this.defaultColor }) {
    this.Custom.customText({ x, y, text, font, fillType, fillColor });
  }

  // 绘制自定义图案（供外部调用）
  customDraw(
    { start, lineWay, fillType = "fill", fillColor, lineWidth },
    save = false
  ) {
    this.Custom.customDraw({ start, lineWay, fillType, fillColor, lineWidth });
    if (save) {
      let ele = {
        id: this.getUniqueId(),
        type: "custom",
        start,
        lineWay,
        fillType,
        fillColor,
        lineWidth,
      };
      this.canvasInfoMap.set(ele.id, ele);
    }
  }
}
